#include "Forest.h"
#include "Weather.h"
#include "../Managers/GameManager.h"
#include "../GameObjects/Player.h"
#include "../Math/Vec3f.h"
#include "../Renderer/DeferredRenderer.h"
#include <sstream>

Forest::Forest(const Vec2i &size)
	: map_size(size)
{
}

Forest::Forest(Vec2i &&size)
	: map_size(size)
{
}

Forest::Forest(int width, int height)
	: map_size(width, height)
{
}

Forest::~Forest()
{
}

void Forest::init()
{
	height_map = std::make_shared<TCODHeightMap>(map_size.x()*2, map_size.y()*2);
	TCODRandom ran(103);
	noise = std::make_shared<TCODNoise>(2, &ran);
	//height_map->addFbm(noise.get(),2.20*map_size.x()/200.0f,2.20*map_size.x()/200.0f,0,0,10.0f,1.0,100.0f);
	//height_map->addHill(map_size.x()/2.0f, map_size.y()/2.0f, 20.0f, 200.0f);
	TCODHeightMap hm1(map_size.x()*2, map_size.y()*2);
	TCODHeightMap hm2(map_size.x()*2, map_size.y()*2);
	hm1.addFbm(noise.get(), 0.5f, 0.75f, 0.001f,0.0001f, 4, 0.0005, 200.0f);
	hm2.addFbm(noise.get(), 4.06f, 4.04f, 0,0, 12, 0.0001f, 20.0f);
	height_map->add(&hm2, &hm1);
	height_map->addFbm(noise.get(), 0.75f, 0.5f, 0.0f,0.0, 2, 0.00001f, 50.0f);

	//smooth the terrain
	int smooth_dx[] = { -1,		-1,		-1,		 0,		0,		0,		 1,		1,		1 };
	int smooth_dy[] = { -1,		 0,		 1,		-1,		0,		1,		-1,		0,		1 };
	float weight[] =  {  0.63f,	 0.63f,	 0.63f,	 0.63f,	0.63f,	0.63f,	 0.63f,	0.63f,	0.63f };
	height_map->kernelTransform(9, smooth_dx, smooth_dy, weight, 0.0f, 220.0f);
	
	//height_map->normalize();

	fov_map = std::make_shared<TCODMap>(map_size.x(), map_size.y());
	fov_map->clear(true, true);

	for(unsigned int y = 0; y < map_size.y(); y++)
	{
		for(unsigned int x = 0; x < map_size.x(); x++)
		{
			float norm_f[3];
			height_map->getNormal(x,y,norm_f);
			Vec3f norm(norm_f[0], norm_f[2], norm_f[1]);
			norm.normalize();
			auto cell = std::make_shared<MapCell>(x, y, height_map->getValue(x,y), height_map->getSlope(x,y), norm);
			cells.push_back(cell);

			if(cell->terrainType->id <= TERRAIN_DIRT)
			{
				auto chance = ran.get(0.0, 1.0);
				if(chance < 0.1)
				{
					auto radius = ran.getInt(1,5);
					bool plant_tree = true;
					for(unsigned int i = 0; i < radius; i++)
					{
						int dx = x-i;
						if(dx >= 0)
						{
							auto index = (dx)+(y)*map_size.x();
							if(cells[index]->canPlantTree() == false)
								plant_tree = false;
							}

						int dy = y-i;
						if(dy >= 0)
						{			
							auto index = (x)+(dy)*map_size.x();
							if(cells[index]->canPlantTree() == false)
								plant_tree = false;
						}

						if(dx >= 0 && dy >= 0)
						{
							auto index = (dx)+(dy)*map_size.x();
							if(cells[index]->canPlantTree() == false)
								plant_tree = false;
						}
					}

					if(plant_tree)
					{
						auto tree = std::make_shared<Tree>(radius);
						trees.push_back(tree);
						//fov_map->setProperties(x,y, false, false);
						cell->setWalkable(false);
						cell->setTreePlanted(tree);
						tree->setPos(cell->getPos());
						for(unsigned int i = 0; i < radius; i++)
						{
							int dx = x-i;
							if(dx >= 0)
							{
								auto index = (dx)+(y)*map_size.x();
								if(cells[index]->canPlantTree())
									cells[index]->setTreePlanted();
							}
							int dy = y-i;
							if(dy >= 0)
							{
								auto index = (x)+(dy)*map_size.x();
								if(cells[index]->canPlantTree())
									cells[index]->setTreePlanted();
							}
							if(dx >= 0 && dy >= 0)
							{
								auto index = (dx)+(dy)*map_size.x();
								if(cells[index]->canPlantTree())
									cells[index]->setTreePlanted();
							}
						}
					}
				}
			}
		}
	}
	
}

void Forest::update()
{
}
//map=(7,3), pp=(3,1),
//vp=(5,3), vp/2=(3,2)
//mv_s=(1,0) = pp - ((vp/2)-1) = (3-(3-1), 1-(2-1)) = (1,0)
//mv_e=(5,2) = pp + ((vp/2)-1) = (3+(3-1), 1+(2-1)) = (5,2)
/**********************
/*00*01*02*03*04*05*06*
/**********************	
/*07*08*09*10*11*12*13*
/**********************
/*14*15*16*17*18*19*20*
/**********************/
void Forest::render()
{
	auto g_buffer = GameManager::getSingleton()->getRenderer();
	//auto console = GameManager::getSingleton()->getMapConsole();
	auto viewport = GameManager::getSingleton()->getMapViewport();
	auto &player_pos = GameManager::getSingleton()->getPlayer()->getPos();
	auto player_bounds = (viewport/2);

	auto map_view_start = player_pos - (player_bounds);
	auto map_view_end = player_pos + (player_bounds);

	float player_altitude = 0.0f;
	int lookup1d_p = player_pos.x()+player_pos.y()*map_size.x();
	if(lookup1d_p < cells.size())
	{
		auto player_cell = cells[lookup1d_p];
		player_altitude = player_cell->getAltitude();
	}

	//fov_map->computeFov(player_pos.x(), player_pos.y(), 40, true, FOV_DIAMOND);

	int x_count = 0;
	int y_count = 0;
	for(int x = map_view_start.x(); x < map_view_end.x()-1; x++)
	{
		for(int y = map_view_start.y(); y < map_view_end.y()-1; y++)
		{
			int lookup1d = x+y*map_size.x();
			if(lookup1d >= cells.size())
			{
				y_count++;
				continue;
			}
			
			auto cell = cells[lookup1d];

			auto pos = Vec2i(x, y);
			g_buffer->addSymbol(pos, cell->terrainType->frgr_symbol, 0.0f, false);

			//Foreground shading
			Vec3f frgr_color(cell->terrainType->frgr_color.r / 255.0f, cell->terrainType->frgr_color.g / 255.0f, cell->terrainType->frgr_color.b / 255.0f);
			g_buffer->shade(pos, frgr_color, DeferredRenderer::RL_FOREGROUND, DeferredRenderer::RT_DIFFUSE);
			g_buffer->shade(pos, Vec3f(0.5f, 0.5f, 0.5f), DeferredRenderer::RL_FOREGROUND, DeferredRenderer::RT_SPECULAR);

			Vec3f bkgr_color(cell->terrainType->bkgr_color.r / 255.0f, cell->terrainType->bkgr_color.g / 255.0f, cell->terrainType->bkgr_color.b / 255.0f);
			g_buffer->shade(pos, bkgr_color, DeferredRenderer::RL_BACKGROUND, DeferredRenderer::RT_DIFFUSE);
			g_buffer->shade(pos, Vec3f(0.5f, 0.5f, 0.5f), DeferredRenderer::RL_BACKGROUND, DeferredRenderer::RT_SPECULAR);
			g_buffer->shade(pos, Vec3f(0.0f, 1.0f, 0.0f), DeferredRenderer::RL_BACKGROUND, DeferredRenderer::RT_NORMAL);
			g_buffer->shade(pos, cell->getAltitude(), DeferredRenderer::RT_DEPTH);
			g_buffer->shade(pos, 0.5f, DeferredRenderer::RT_SHININESS);
			g_buffer->setMapInfo(pos, true, cell->isWalkable());

			auto tree = cell->getTree();
			if(tree)
				tree->render();	

			y_count++;
		}
		y_count=0;
		x_count++;
	}
}

const MapCellPtr &Forest::getCellAt(const Vec2i &pos)
{
	auto index = pos.x()+pos.y()*map_size.x();
	if(index < cells.size())
		return cells[index];
	else
		return null_cell;
}
